#include <linux/module.h> 
#include <linux/kernel.h> 
#include <linux/init.h> 
#include <linux/proc_fs.h> 
#include <linux/sched.h> 
#include <asm/uaccess.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>

/* 
   set val format: 
       echo "0x01=0xAF" > addr
   read val format:
       1. echo "0x01"(the val you want) > addr
	   2. cat addr
	   3. read all reg: cat all
 */
#define MODULE_NAME "debugfs_example"
#define PMIC_NUM_REG    10
#define PMIC_READ_STRLEN  4
#define PMIC_WRITE_STRLEN  9

static struct dentry *pmic_dbgfs_root;
struct dentry *entry = NULL;
int reg = 0;

static int pmic_addr_show(struct seq_file *seq, void *v)
{
	int val = 0;

	/* TODO: read val */
	seq_printf(seq, "0x%x=0x%x\n", reg, val);
    return 0;
}

static int pmic_addr_open(struct inode *inode, struct file *file)
{
	return single_open(file, pmic_addr_show, NULL);
}

static ssize_t pmic_addr_write(struct file *filp, const char __user *buffer,
					   size_t count, loff_t *ppos)
{
	char buf[32] = {0};
	int val = 0, len = 0;

    if (copy_from_user(buf, buffer, count))
        return -EFAULT;

	printk(KERN_INFO "%s: count = %d, *ppos = %lld, buf = %s\n", __func__, count, *ppos, buf); 
	len = count - 1;

	if (len == PMIC_READ_STRLEN) {
		sscanf(buf, "0x%x", &reg);
		printk(KERN_INFO "ready to read reg 0x%x\n", reg);
	} else if (len == PMIC_WRITE_STRLEN) {
		sscanf(buf, "%x=%x", &reg, &val);
		printk(KERN_INFO "ready to write 0x%x=0x%x\n", reg, val);
	} else {
		printk(KERN_INFO "Wrong set.\n");
		return -1;
	}

    *ppos += count;
 
    return count;
}

struct file_operations addr_fops = {
    .owner = THIS_MODULE,
    .open = pmic_addr_open,
    .read = seq_read,
    .write = pmic_addr_write,
};

static int pmic_all_show(struct seq_file *seq, void *v)
{
	int i = 0;
	int val = 0;
	for (i = 0; i < PMIC_NUM_REG; ++i) {
		reg = i; val = i;		/* TODO: read all reg */
		seq_printf(seq, "0x%x=0x%x\n", reg, val);
	}

    return 0;
}

static int pmic_all_open(struct inode *inode, struct file *file)
{
	return single_open(file, pmic_all_show, NULL);
}

struct file_operations all_fops = {
    .owner = THIS_MODULE,
    .open = pmic_all_open,
    .read = seq_read,
};
 
static void pmic_create_debugfs(int chip)
{
	struct dentry *entry;

	pmic_dbgfs_root = debugfs_create_dir("pmic_debug", NULL);
	if (IS_ERR(pmic_dbgfs_root)) {
		printk(KERN_INFO "DEBUGFS DIR create failed\n");
		return ;
	}

	entry = debugfs_create_file("all", 0644, pmic_dbgfs_root, NULL, &all_fops);
	if (IS_ERR(entry)) {
		debugfs_remove_recursive(pmic_dbgfs_root);
		pmic_dbgfs_root = NULL;
		printk(KERN_INFO "DEBUGFS entry Create failed\n"); /* TODO: dev */
		return ;
	}

	entry = debugfs_create_file("addr", 0644, pmic_dbgfs_root, NULL, &addr_fops);
	if (IS_ERR(entry)) {
		debugfs_remove_recursive(pmic_dbgfs_root);
		pmic_dbgfs_root = NULL;
		printk(KERN_INFO "DEBUGFS entry Create failed\n");
		return ;
	}
}

static inline void pmic_remove_debugfs(int chip)
{
	if (pmic_dbgfs_root)
		debugfs_remove_recursive(pmic_dbgfs_root);
}

/*模块初始化函数*/ 
static int __init init_debugfs_example(void) 
{
	/* Create debugfs for maxim registers */
	int chip = 0;
    printk(KERN_INFO "%s init\n", MODULE_NAME);  

	pmic_create_debugfs(chip);

    return 0; 
}

/*模块卸载函数*/ 
static void __exit cleanup_debugfs_example(void) 
{
	if (pmic_dbgfs_root)
		debugfs_remove_recursive(pmic_dbgfs_root);

    printk(KERN_INFO "%s removed\n", MODULE_NAME); 
}

module_init(init_debugfs_example); 
module_exit(cleanup_debugfs_example); 
MODULE_AUTHOR("xxx"); 
MODULE_DESCRIPTION("debugfs examples");
MODULE_LICENSE("GPL");
